The XA (eXtended Architecture) protocol relates to transactions that consist of multiple operations that access resources. For example, a banking application may conduct an XA transaction that consists of two operations (1) deduct money from a first bank account and (2) add money to a second bank account. Typically, either both of the operations relating to the XA transaction will be permanent, if successful, or none of them will be committed.
In traditional data storage systems, consistency is usually achieved by a data locking mechanism to prevent data from being corrupted or invalidated when multiple users try to write to the same data. When a lock of the data is acquired for a transaction, the transaction has access to the locked data until the lock is released. Other transactions may not have access to the locked data. The XA standard uses a two-phase commit (2PC) protocol to ensure that all resources enlisted within a transaction either commit or rollback to a previous state. The first phase is preparation, which may include acquiring a lock on the data for the transaction to prevent other transactions from accessing that data, and updating the values (e.g., deduct money, add money) for the locked data to reflect the transaction. If preparation is successful, the second phase of commitment can be initiated, which may include releasing the locks to allow other transactions access to the data that was updated. A pessimistic locking approach typically acquires locks with each write operation of a transaction. For example, a lock may be acquired when the first bank account balance is changed and a lock may be acquired when the second account balance is changed. In an optimistic locking approach, locks are usually not acquired until during the prepare phase.
A deadlock may occur when two transactions that write to the same data execute concurrently or execute nearly at the same time. A deadlock is a situation wherein two or more competing actions are each waiting for the other to finish, and thus, neither transaction finishes. For example, a first transaction (TX1) wishes to acquire locks on Data-A and Data-B in that order. A second transaction (TX2) wishes to acquire locks on Data-B and Data-A in that order. If the transactions run in parallel, TX1 may obtain a lock on Data-A, and TX2 may obtain a lock on Data-B. TX1 would like to progress and acquire a lock on Data-B, but would not be able to do so since Data-B is already locked by TX2. Similarly, TX2 would try to acquire a lock on Data-A, but would not be able to do so since Data-A is already locked by TX1. Each transaction waits for the other transaction to finish causing a deadlock.
Traditional solutions typically wait for a deadlock to occur and then build a dependency graph describing the dependencies between the deadlocked transactions. Generally, conventional solutions terminate one of the two deadlocked transactions. Such traditional solutions may be quite costly because they involve a large amount of CPU and network usage, which is not ideal. Such solutions are generally also not fast enough in terminating a deadlocked transaction.